#include "Race.h"
using namespace std;

DebugLevelType Race::elementaire  = BLANK;
DebugLevelType Race::fonction     = BLANK;

/**
 * Constructeur standard. Est utilisé lors de la création d'une Race au
 * travers de l'interface graphique et du mode console. Initialise les
 * pointeurs à zéro et le nom de la Race.
 * @param type nom de la race
 */
Race::Race(const string& type)
{
  Type = type;
  ptrCapa      = 0;
  ptrNbrCapa   = 0;
  ptrEns       = 0;
  ptrNbrEns    = 0;
  ptrMaxEns    = 0;
  ptrNomEns    = 0;
  ptrPreRequis = 0;
}

/**
 * Constructeur de copie, réalise une copie profonde. Est utilisé lors
 * de la création d'un personnage (à partir d'une Race et d'une
 * Classe). Les capacités, les pointeurs sont copiés.
 *
 * @param race race à copier
 */
Race::Race(const Race& race)
{
  capacites.assign( race.capacites.begin(), race.capacites.end() );
  Type = race.Type;
  ptrCapa      = race.ptrCapa;
  ptrNbrCapa   = race.ptrNbrCapa;
  ptrEns       = race.ptrEns;
  ptrNbrEns    = race.ptrNbrEns;
  ptrMaxEns    = race.ptrMaxEns;
  ptrNomEns    = race.ptrNomEns;
  ptrPreRequis = race.ptrPreRequis;
}

/**
 * Destructeur, ne fait rien de spécial.
 */
Race::~Race()
{}

/**
 * Méthode qui n'applique aucun ajustement, pour les races sans
 * ajustement. Est redéfinie pour les races ayant des ajustements
 * (toutes sauf Humain et DemiElfe)
 * @param tab tableau contenant les 6 traits
 */
void Race::appliquerAjustements(unsigned int tab[7])
{}

/**
 * Copie les capacités raciales dans le flot passé en argument. Par
 * défaut on réalise ici un affichage sur la sortie standard. Mais on
 * peut également récupérer les informations pour faire un affichage
 * dans une fenêtre.\n
 *
 * Fait appel à getCapacite().\n
 *
 * Le niveau de sortie est paramétré par Race::fonction :\n
 * \e FUNC permet d'afficher le nom de la méthode en début et fin
 * d'exécution\n
 * \e LFUNC permet d'afficher de numéro de chaque capacité\n
 * \e DEBUG permet d'afficher le nombre de capacités\n
 *
 * @param os flot, par défaut blank() est utilisé
 */
void Race::afficheInfo(ostream& os) const
{
  Debug::empileNiveau( &fonction );
  //DebugLevelType etat_precedent(dout.getLevel());
  //dout.setLevel(fonction);
  func() << "Race::afficheInfo -> début" << endreq;

  os << "Race : " << Type << endreq;

  unsigned int taille( getTaille() );

  debug() << "Nbr de capacités " << taille << endreq;

  for (unsigned int i(0); i < taille; i++)
    {
      lfunc() << i << "-ème capacité " << getCapa(i) << endreq;
      os << getCapacite(i) << endreq;
    }


  func() << "Race::afficheInfo -> fin" << endreq;
  Debug::depileNiveau();//dout.setLevel(etat_precedent);
}

/**
 * Retourne la race (càd le nom).
 * @return le nom de la race
 */
string Race::getType() const
{
  return Type;
}

/**
 * Retourne le nombre de capacités raciales possédées par le personnage.
 * @return la dimension du conteneur capacites.
 */
unsigned int Race::getTaille() const
{
  return capacites.size();
}

/**
 * Retourne la capacité désirée. Si l'on demande une capacité
 * inexistante, par exemple en demandant la capacité numéro 10 et que
 * le nombre de capacités est 5, un message d'erreur est affiché et on
 * retourne le nombre 65535 (avant ce nombre était encodé dans un
 * short unsigned).\n
 *
 * Le niveau de sortie est paramétré par Race::élémentaire :\n
 * \e FUNC permet d'afficher le nom de la méthode en début et fin
 * d'exécution\n
 *
 * @param i le numéro de la capacité
 * @return la position de la capacité dans la liste
 */
unsigned int Race::getCapa(const unsigned int& i) const
{
  Debug::empileNiveau( &elementaire );
  //DebugLevelType etat_precedent(dout.getLevel());
  //dout.setLevel(elementaire);
  func() << "Race::getCapa -> début" << endreq;

  if (i < capacites.size())
    {
      func() << "Race::getCapa -> fin" << endreq;
      Debug::depileNiveau();//dout.setLevel(etat_precedent);
      return capacites[i];
    }
  else
    {
      error() << "Compétence inexistante, dépassement de portée" << endreq;
      func() << "Race::getCapa -> fin" << endreq;
      Debug::depileNiveau();//dout.setLevel(etat_precedent);
      return 65535;
    }
}

/**
 * Retrouve la capacité raciale désirée. Fonctionne sur le même
 * principe que getCapa(), mais renvoie le nom de la capacité au lieu
 * de son numéro. Un message d'erreur est affiché si la capacité est
 * inexistante, et on retourne une string vide.\n
 *
 * Le niveau de sortie est paramétré par Race::élémentaire :\n
 * \e FUNC permet d'afficher le nom de la méthode en début et fin
 * d'exécution
 *
 * @param i numéro de la capacité
 * @return la capacité (string)
 */
string Race::getCapacite(const unsigned int& i) const
{
  Debug::empileNiveau( &elementaire );
  //DebugLevelType etat_precedent(dout.getLevel());
  //dout.setLevel(elementaire);
  func() << "Race::getCapacite -> début" << endreq;

  if (i >= *ptrNbrCapa)
    {
      warning() << "Tentative d'accéder à une capacité inconnue" << endreq;
      func() << "Race::getCapacite -> fin" << endreq;
      Debug::depileNiveau();//dout.setLevel(etat_precedent);
      return "";
    }
  else
    {
      func() << "Race::getCapacite -> fin" << endreq;
      Debug::depileNiveau();//dout.setLevel(etat_precedent);
      return ptrCapa -> at( capacites[ i ] ) . Capa;
    }
}

/**
 * Permet de récupérer un pointeur sur les capacités que l'instance de
 * Race possède.
 *
 * @return un pointeur sur capacites.
 */
const vector< unsigned int >* Race::getCapacitesAdresse() const
{
  return &capacites;
}

/**
 * Réalise une copie de toutes les capacités de la Race
 * considérée. Est utilisée lors de la création de la Race, en mode
 * console ou graphique, sert également lors de l'écriture des
 * feuilles de perso.\n
 * Le niveau de sortie est paramétré par Race::elementaire :\n
 * \e FUNC permet d'afficher le nom de la méthode en début et fin
 * d'exécution
 *
 * @param nbr contient le nombre de capacités
 * @param capa contient les capacités
 */
void Race::getCapacites(unsigned int& nbr, vector<ListCapa>& capa) const
{
  Debug::empileNiveau( &elementaire );
  func() << "Race::getCapacites -> début" << endreq;
  
  nbr = *ptrNbrCapa;

  capa.assign( ptrCapa -> begin(), ptrCapa -> end() );

  func() << "Race::getCapacites -> fin" << endreq;
  Debug::depileNiveau();
}

/**
 * Permet de récupérer un pointeur sur l'ensemble des capacités
 * raciales. L'utilisation est la même que pour l'auter méthode du
 * même nom.
 *
 * @return ptrCapa.
 */
const vector< ListCapa >* Race::getCapacites() const
{
  return ptrCapa;
}

/**
 * Permet de récupérer le nombre d'ensembles raciaux, ainsi que le
 * nombre maximal de compétences dans un ensemble.
 *
 * @param nbr nombre d'ensembles (écrit par la méthode).
 * @param max nombre maximal de compétences dans un ensemble (écrit
 * par la méthode).
 */
void Race::getDonneesEnsRaciaux(unsigned int& nbr, unsigned int& max) const
{
  nbr = *ptrNbrEns;
  max = *ptrMaxEns;
}

/**
 * Permet de récupérer un pointeur sur normalement le premier ensemble
 * racial.
 *
 * @return pointeur sur le conteneur des noms des ensembles raciaux.
 */
const vector< ListCapa >* Race::getNomsEnsemblesRaciaux() const
{
  return ptrNomEns;
}

/**
 * Permet de récupérer un pointeur sur l'ensemble racial désiré.
 *
 * @param nbr numéro de l'ensemble.
 * @return pointeur sur l'ensemble désiré. On vérifie d'abord si
 * l'ensemble est défini; dans le cas où l'ensemble n'existe pas, on
 * renvoie un pointeur nul.
 */
const vector<unsigned int>* Race::getEnsembleRacial(const unsigned int& nbr)
  const
{
  if (nbr >= *ptrNbrEns)
    {
      warning() << "Tentative d'accéder à un ensembe racial non existant" 
		<< endreq;
      return 0;
    }
  return & (ptrEns -> at(nbr)); 
}

/**
 * Permet de récupérer le nombre de PP disponible lors de la création
 * d'une Race.
 *
 * @return nombre de PP à disposition.
 */
unsigned int Race::getPP() const
{
  if (Type == "Humain")
    return 10;
  else if (Type == "Demi-Elfe")
    return 25;
  else if (Type == "Demi-Ogre")
    return 15;
  else if (Type == "Demi-Orque")
    return 15;
  else if (Type == "Elfe")
    return 45;
  else if (Type == "Gnome")
    return 45;
  else if (Type == "Hobbit")
    return 35;
  else if (Type == "Nain")
    return 45;
  else
    return 0;
}

/**
 * Permet de fixer les capacités raciales une fois le personnage
 * créé. Est utilisé lors de la création d'une Race en lisant un
 * fichier XML (en mode console ou graphique).
 *
 * @param capa conteneur des capacités.
 */
void Race::setCapacites(const vector<unsigned int>& capa)
{
  capacites.assign( capa.begin(), capa.end() );
}

/**
 * Gestion des niveau de sortie des fonctions.
 * @param level le niveau (INFO, DEBUG, ...).
 * @param n permet de sélectionner quel variable est affectée.
 * @see DebugLevelType
 */
void Race::setLevel(const DebugLevelType& level, const unsigned int& n)
{
  switch(n)
    {
    case 1:
      elementaire = level;
      break;
    case 2:
      fonction = level;
      break;
    default:
      warning() << "Variable inconnue, dans Race::setLevel " << n << endreq;
    }
}

/**
 * Retrouve les capacités raciales. Les numéros de ces capacités
 * sont retournées.
 *
 * @param nbr nombre de capacités.
 * @return un pointeur sur le conteneur des capacités (en lecture
 * seule).
 */
const vector<unsigned int>* Race::getToutesCapacites(unsigned int& nbr) const
{
  nbr = capacites.size();
  
  return &capacites;
}

/**
 * Permet de créer une Race quelconque en sélectionnant les
 * capacités. Fonctionne comme les anciens constructeurs des classes
 * dérivées.
 */
void Race::construitRace(unsigned int& pps)
{
  Debug::empileNiveau( &elementaire );
  func() << "Race::construitRace -> début" << endreq;

  unsigned int i, choix( *ptrNbrEns + 1 ), confirmation, savePP( pps ),
    nbrActuel;

  if ( ptrNomEns != 0 )
    {
      do
	{
	  pps = savePP;
	  blank() << "Sélectionner l'ensembla racial souhaité :" << endreq;
	  for (i = 0; i < *ptrNbrEns; i++)
	    blank() << i + 1 <<' ' << ptrNomEns -> at( i ) .Capa
		    << " (pour " << ptrNomEns -> at( i ) .Cout << " pp)"
		    << endreq;
	  blank() << *ptrNbrEns + 1 << " Aucun" << endreq;

	  choix = demanderUnsignedBorne( "(" + ecrireString( pps ) +
					 "pp) Sélection : ",
					 1, *ptrNbrEns + 1 );

	  if ( choix != *ptrNbrEns + 1 )
	    {
	      if ( pps >= ptrNomEns -> at( choix - 1 ) .Cout )
		{
		  blank() << "Ensemble sélectionné : "
			  << ptrNomEns -> at( choix - 1 ) .Capa << endreq;
		  pps -= ptrNomEns -> at( choix - 1 ) .Cout;
		}
	      else
		{
		  warning() << "Pas assez de pp" << endreq;
		  continue;
		}
	    }
	  else
	    blank() << "Aucun ensemble sélectionné" << endreq;

	  blank() << "Entrer 1 pour continuer, 2 pour annuler" << endreq;
	  confirmation = demanderUnsignedBorne( "Choix : ", 1, 2 );
	}
      while ( confirmation == 2 );
    }

  savePP = pps;

  if ( choix == *ptrNbrEns + 1 )
    {
      // Il faut afficher toutes les compétences et faire une sélection.

      blank() << "Slectionner les capacités raciales :" << endreq;
      for (i = 0; i < *ptrNbrCapa; i++)
	blank() << i + 1 << ' ' << ptrCapa -> at( i ) .Capa
		<< " (pour " << ptrCapa -> at( i ) . Cout << ")" << endreq;
      blank() << *ptrNbrCapa + 1 << " Aucune" << endreq;

      do
	{
	  capacites.clear();
	  pps = savePP;
	  do
	    {
	      choix = demanderUnsignedBorne( "(" + ecrireString( pps ) +
					     "pp) Sélection : ",
					     1, *ptrNbrCapa + 1 );
	      
	      if ( choix != *ptrNbrCapa + 1 )
		{
		  if ( pps >= ptrCapa -> at( choix - 1 ) .Cout )
		    {
		      if ( safePushBack( &capacites, choix - 1 ) )
			warning() << "Compétence déjà prise" << endreq;
		      else
			{
			  blank() << "Capacité sélectionnée : "
				  << ptrCapa -> at( choix - 1 ) .Capa
				  << endreq;
			  pps -= ptrCapa -> at( choix - 1 ) .Cout;
			  capacites.push_back( choix - 1 );
			}
		    }
		  else
		    {
		      warning() << "Pas assez de pp" << endreq;
		      continue;
		    }
		}
	    }
	  while ( choix != *ptrNbrCapa + 1 );

	  nbrActuel = capacites.size();

	  if ( nbrActuel > 0 )
	    {
	      blank() << "Récapitulatif des capacités: " << endl;
	      afficheInfo( blank() );
	    }
	  else
	    blank() << "Aucune capacité sélectionnée" << endreq;

	  blank() << "Entrer 1 pour continuer, 2 pour annuler" << endreq;
	  confirmation = demanderUnsignedBorne( "Choix : ", 1, 2);
	}
      while ( confirmation == 2 );
    }
  else
    {
      // On a sélectionné un ensemble
      for (i = 0; i < *ptrMaxEns; i++)
	{
	  if ( (ptrEns -> at( choix - 1))[i] == 0 )
	    break;
	  capacites.push_back( (ptrEns -> at( choix - 1))[i] );
	}
    }

  func() << "Race::construitRace -> fin" << endreq;
  Debug::depileNiveau();
}

/**
 * Permet de savoir si on peut construire la race voulue avec les
 * traits donnés.
 *
 * @param tab tableau des traits.

 * @return StatusCode, \e true si possible, \e false sinon. Le champ
 * Valeur contient le numéro du trait qui empêche la création et
 * Message une indication.
 */

StatusCode Race::rempliPrerequis(unsigned int tab[7])
{
  if ( ptrPreRequis == 0 )
    {
      appliquerAjustements( tab );
      return StatusCode( true );
    }
  else
    {
      vector< Prerequis >::const_iterator it, Begin( ptrPreRequis -> begin() ),
	End( ptrPreRequis -> end() );

      StatusCode sc;

      for (it = Begin; it != End; it++)
	{
	  sc = it -> estOK( tab );
	  if ( ! sc )
	    return sc;
	}
      appliquerAjustements( tab );
      return sc;
    }
}
